using System;
using System.Collections.Generic;
using NetProtocol.Enum;
using NetProtocol.POD;

namespace IQIGame.Onigao.Logic
{
    /// <summary>
    /// 数据同步模块.
    /// 注意：不能先添加到缓存然后在update的时候合批发送，可能导致时序错误。
    /// </summary>
    public class LogicDataSyncModule
    {
        /// <summary>
        /// 事件处理器
        /// </summary>
        private readonly Dictionary<DataSyncType, Delegate> handlerDic = new Dictionary<DataSyncType, Delegate>();

        public static LogicDataSyncModule Instance { get; } = new LogicDataSyncModule();

        private LogicDataSyncModule()
        {
        }

        #region API

        /// <summary>
        /// 注册一个事件处理器。
        /// 事件处理器可通过<see cref="SLevelLogicModule.GetPlayerData"/>获取玩家数据
        /// </summary>
        /// <param name="type">事件类型</param>
        /// <param name="action">处理函数</param>
        public void RegisterHandler(DataSyncType type, Action<DataSyncEventPOD> action)
        {
            handlerDic.Add(type, action); // 重复会抛异常
        }

        /// <summary>
        /// 注册一个事件处理器，事件处理器只在玩家在线的情况下调用。
        /// </summary>
        /// <param name="type">事件类型</param>
        /// <param name="action">处理函数</param>
        public void RegisterHandler(DataSyncType type, Action<LogicPlayer, DataSyncEventPOD> action)
        {
            handlerDic.Add(type, action); // 重复会抛异常
        }

        /// <summary>
        /// 压入一个同步事件
        /// (该方法会自动设置pid)
        /// </summary>
        /// <param name="logicPlayer">玩家</param>
        /// <param name="update">更新事件</param>
        public void Push(LogicPlayer logicPlayer, DataSyncEventPOD update)
        {
            update.pid = logicPlayer.id;
            Push(logicPlayer.gameServerId, update);
        }

        /// <summary>
        /// 压入多个同步事件
        /// (该方法会自动设置pid)
        /// </summary>
        /// <param name="logicPlayer">玩家</param>
        /// <param name="updates">更新操作</param>
        public void Push(LogicPlayer logicPlayer, List<DataSyncEventPOD> updates)
        {
            foreach (DataSyncEventPOD update in updates)
            {
                update.pid = logicPlayer.id;
            }
            Push(logicPlayer.gameServerId, updates);
        }

        /// <summary>
        /// 压入一个同步事件
        /// </summary>
        /// <param name="gameServerId">游戏服id</param>
        /// <param name="update">更新事件</param>
        public void Push(string gameServerId, DataSyncEventPOD update)
        {
            update.gameSeverId = gameServerId;
            update.logicSeverId = LogicHelper.LogicServerId;
            LogicHelper.Router.SendToGame(gameServerId, update);
        }

        /// <summary>
        /// 压入多个同步事件
        /// </summary>
        /// <param name="gameServerId">游戏服id</param>
        /// <param name="updates">更新操作</param>
        public void Push(string gameServerId, List<DataSyncEventPOD> updates)
        {
            foreach (DataSyncEventPOD update in updates)
            {
                update.gameSeverId = gameServerId;
                update.logicSeverId = LogicHelper.LogicServerId;
                LogicHelper.Router.SendToGame(gameServerId, update);
            }
        }

        #endregion

        #region MyRegion

        /// <summary>
        /// 该接口由主循环调用
        /// （事件驱动）
        /// </summary>
        /// <param name="eventPod"></param>
        public void OnSyncDataEvent(DataSyncEventPOD eventPod)
        {
            if (!handlerDic.TryGetValue(eventPod.type, out Delegate objAction))
            {
                LogicLog.LogWarning("unsupported data event type: " + eventPod.type);
                return;
            }
            try
            {
                ProcessEvent(objAction, eventPod);
            }
            catch (Exception ex)
            {
                LogicLog.LogException(ex);
                LogicLog.LogWarning($"process event caught exception, event type:{eventPod.type}, pid: {eventPod.pid}");
            }
        }

        private static void ProcessEvent(Delegate objAction, DataSyncEventPOD eventPod)
        {
            if (objAction is Action<DataSyncEventPOD> action1)
            {
                action1(eventPod);
            }
            else
            {
                Action<LogicPlayer, DataSyncEventPOD> action2 = (Action<LogicPlayer, DataSyncEventPOD>)objAction;
                LogicPlayer logicPlayer = SLevelLogicModule.Instance.GetPlayerData(eventPod.pid);
                if (logicPlayer == null || logicPlayer.playData.reason != ConnectLogicReason.TryEnterLevel)
                {
                    // 玩家很可能不在，因此不打印日志；非关卡内部处理同步
                    // LogicLog.Log("player is offline, pid: " + eventPod.pid);
                    return;
                }
                action2(logicPlayer, eventPod);
            }
        }

        public void Init()
        {
            // 统一管理注册，保持和java相同实现 -- java分散注册有坑
            {
                LogicItemModule itemModule = LogicItemModule.Instance;
                RegisterHandler(DataSyncType.AddItem, itemModule.SyncEvent_AddItem);
                RegisterHandler(DataSyncType.CostItem, itemModule.SyncEvent_CostItem);
                RegisterHandler(DataSyncType.DelItem, itemModule.SyncEvent_DelItem);

                RegisterHandler(DataSyncType.AddBasicItem, itemModule.SyncEvent_AddBasicItem);
                RegisterHandler(DataSyncType.CostBasicItem, itemModule.SyncEvent_CostBasicItem);

                RegisterHandler(DataSyncType.IncItemDebts, itemModule.SyncEvent_IncItemDebts);
                RegisterHandler(DataSyncType.DecItemDebts, itemModule.SyncEvent_DecItemDebts);

                RegisterHandler(DataSyncType.UpdateItems, itemModule.SyncEvent_UpdateItems);
                RegisterHandler(DataSyncType.UpdateItemCdInfo, itemModule.SyncEvent_UpdateItemCdInfo);

                RegisterHandler(DataSyncType.UseItem, itemModule.SyncEvent_UseItem);
                RegisterHandler(DataSyncType.UseEffectItem, itemModule.SyncEvent_UseEffectItem);
                
                RegisterHandler(DataSyncType.UpdateQuickBarItem, itemModule.SyncEvent_UpdateQuickBarItem);
            }
            // 玩家基础信息
            {
                LogicPlayerModule playerModule = LogicPlayerModule.Instance;
                RegisterHandler(DataSyncType.UpdatePlayerInfo, playerModule.SyncEvent_UpdatePlayerInfo);
                RegisterHandler(DataSyncType.IncParams, playerModule.SyncEvent_IncPlayerParams);
                RegisterHandler(DataSyncType.UpdateParams, playerModule.SyncEvent_UpdatePlayerParams);
            }
            // 英雄和编队
            {
                LogicPlayerModule playerModule = LogicPlayerModule.Instance;
                RegisterHandler(DataSyncType.UpdateHeroes, playerModule.SyncEvent_UpdateHeroes);
                RegisterHandler(DataSyncType.UpdateFormation, playerModule.SyncEvent_UpdateFormation);
                
                RegisterHandler(DataSyncType.AddPet, playerModule.SyncEvent_AddPet);
                RegisterHandler(DataSyncType.UpdatePet, playerModule.SyncEvent_UpdatePet);
                RegisterHandler(DataSyncType.RemovePet, playerModule.SyncEvent_RemovePet);
            }
            // 任务
            {
                LogicQuestModule questModule = LogicQuestModule.Instance;
                RegisterHandler(DataSyncType.AddQuest, questModule.SyncEvent_AddQuest);
                RegisterHandler(DataSyncType.UpdateQuest, questModule.SyncEvent_UpdateQuest);
                RegisterHandler(DataSyncType.UpdateQuestProgress, questModule.SyncEvent_UpdateProgress);
                RegisterHandler(DataSyncType.DelQuest, questModule.SyncEvent_DelQuest);
                
                RegisterHandler(DataSyncType.AddCommitedQuests, questModule.SyncEvent_AddCommittedQuests);
                RegisterHandler(DataSyncType.DelCommitedQuests, questModule.SyncEvent_DelCommittedQuests);
                RegisterHandler(DataSyncType.AddFailedQuests, questModule.SyncEvent_AddFailedQuests);
                RegisterHandler(DataSyncType.DelFailedQuests, questModule.SyncEvent_DelFailedQuests);
                RegisterHandler(DataSyncType.UpdateTrackQuest, questModule.SyncEvent_UpdateTrackQuest);
            }
        }

        #endregion
    }
}